package com.plian.system.service.hr.dept.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.listener.PageReadListener;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.plian.Tools.*;
import com.plian.Tools.pagedata.PageData;
import com.plian.system.bean.dept.DeptExcelBean;
import com.plian.system.cache.DeptCache;
import com.plian.system.constant.sys.FormTypeConstant;
import com.plian.system.dao.sys.orgzation.OrgzationDao;
import com.plian.system.entity.hr.dept.Dept;
import com.plian.system.entity.hr.staff.StaffFiles;
import com.plian.system.entity.sys.FormHistoryRecord;
import com.plian.system.excel.ImportExcelUtil;
import com.plian.system.excel.ImportResult;
import com.plian.system.mapper.hr.dept.DeptMapper;
import com.plian.system.mapper.hr.staff.StaffFilesMapper;
import com.plian.system.mybatis.base.BaseServiceImpl;
import com.plian.system.mybatis.support.Condition;
import com.plian.system.service.hr.dept.IDeptService;
import com.plian.system.service.sys.system.IFormHistoryRecordService;
import com.plian.system.vo.hr.dept.DeptVO;
import com.plian.system.wrapper.hr.dept.DeptWrapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

/**
 * 部门基础信息 服务实现类
 *
 * @author gangan
 * @since 2019-08-07
 */
@Service
@AllArgsConstructor
public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, Dept> implements IDeptService {

	private OrgzationDao orgzationDao;

	private StaffFilesMapper staffFilesMapper;

	private IFormHistoryRecordService formHistoryRecordService;

	private DeptWrapper deptWrapper;

	@Data
	@AllArgsConstructor
	private class DeptNode {
		@JsonSerialize(using = ToStringSerializer.class)
		private Long id;
		@JsonSerialize(using = ToStringSerializer.class)
		private Long parent;
		private String code;
		private String name;
		private String deptAffiliation;
		private String deptAffiliationName;
		private List<DeptNode> children;
	}

	@Override
	public IPage<DeptVO> selectDeptPage(IPage<DeptVO> page, DeptVO dept) {
		return page.setRecords(baseMapper.selectDeptPage(page, dept));
	}

	@Override
	@Transactional(rollbackFor=RuntimeException.class)
	public boolean saveOrUpdate(Dept entity) {
		if (entity.getId() == null){
			entity.setFormType(String.valueOf(FormTypeConstant.HR_DEPT));
		}
		if (Optional.ofNullable(entity.getDeptEnableDisable()).isPresent() && entity.getDeptEnableDisable() == 1){
			entity.setDeptDeactivationTime(null);
		}

		Dept old = getById(entity.getId());
		boolean isOk = super.saveOrUpdate(entity);
		if (isOk && entity.getId() != null){
			DeptCache.update(entity.getId());
			if (!entity.equals(old)){
				ObjectMapper mapper=new ObjectMapper();
				try {
					formHistoryRecordService.submit(FormHistoryRecord.builder()
							.record(mapper.writeValueAsString(deptWrapper.entityToVO(old))).recordId(old.getId())
							.formType(String.valueOf(FormTypeConstant.HR_DEPT)).build());
				}catch (Exception e){
					log.error("部门--> 历史数据版本保存失败");
				}

			}
		}
		return isOk;
	}

	@Override
	public List<Object> getTreeList() {
		List<DeptVO> deptVoList = deptWrapper.entityToVO(list());

		return tree(deptVoList);
	}
	@Override
	public String checkName(Dept dept) {
		List<Dept> deptList = list(new LambdaQueryWrapper<Dept>().eq(Dept::getCreateCompanyId, TokenUtil.getTokenOrgId()));
		String judgeName = dept.getDeptName() + dept.getDeptAffiliation();
		boolean isDup = deptList.stream().anyMatch(x -> (x.getDeptName() + x.getDeptAffiliation()).equals(judgeName) && !x.getId().equals(dept.getId()));
		if (isDup) {
			return "名称重复！";
		} else {
			return null;
		}
	}

	@Override
	public String checkCode(Dept dept) {
		List<Dept> deptList = list(new LambdaQueryWrapper<Dept>().eq(Dept::getCreateCompanyId, TokenUtil.getTokenOrgId()));
		String judgeCode = dept.getDeptCode() + dept.getDeptAffiliation();
		boolean isDup = deptList.stream().anyMatch(x -> (x.getDeptCode() + x.getDeptAffiliation()).equals(judgeCode) && !x.getId().equals(dept.getId()));
		if (isDup) {
			return "编码重复";
		} else {
			return null;
		}
	}

	@Override
	public String checkDeptEnableDisable(Dept dept) {
		if (!Optional.ofNullable(dept.getDeptEnableDisable()).isPresent()){
			return "启用/停用，未勾选";
		}

		if (dept.getId() == null || dept.getDeptEnableDisable() == 1){
			return null;
		}

		LambdaQueryWrapper<StaffFiles> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.in(StaffFiles::getDepartmentNameId,dept.getId());
		if(CollectionUtil.isNotEmpty(staffFilesMapper.selectList(queryWrapper))){
			return "已被引用，不可停用";
		}

		if(isHavaChild(Func.toLong(dept.getId()))) {
			return "存在下级部门，不可停用";
		}
		return null;
	}


	@Override
	public List<Object> getTreeInUnitList(String id) {
		List<DeptVO> deptVoList = deptWrapper.entityToVO(baseMapper.selectList(new LambdaQueryWrapper<Dept>()
				.eq(Dept::getDeptAffiliation,id).eq(Dept::getDeptEnableDisable,1)));
		return tree(deptVoList);
	}

	@Override
	public List<Object> getTreeInParent(long id) {
		List deptVoList = getDeptVOS(id);
		return deptVoList;
	}

	@Override
	public File exportExcel(JSONObject jsonObject) {
		List<Dept> depts = list(Condition.getQueryWrapper(new Dept()));
		//return ExcelUtils.exportExcelByList(jsonObject,depts);
		return null;
	}

	@Override
	@Async("ttlExecutor")
	public Future<ImportResult> importExcel(String progressId, InputStream inputStream) {
		String progress = "progress";
		String key = progressId + "#half:hour";
		try {
			byte[] bytes = IOUtils.toByteArray(inputStream);
			inputStream = new ByteArrayInputStream(bytes);
            List<Object> deptReadExcels = new ArrayList<>();
            EasyExcel.read(inputStream).build()
                    .read(EasyExcel.readSheet(1).headRowNumber(1).head(DeptExcelBean.class)
                            .registerReadListener(new PageReadListener<>(list -> deptReadExcels.addAll(list))).build());
			inputStream.reset();
			int size=deptReadExcels.size();
			List<Dept> depts = list();
			ImportResult importResult = checkData(deptReadExcels,depts,key);
			if (!importResult.isSuccess()){
				CacheUtil.put(progress, progress, key, importResult.getMsg());
				return new AsyncResult<>(importResult);
			}
			int count=0;
			Map<String,Dept> deptMap = new HashMap<String,Dept>();
			for(Dept dept : depts){
				deptMap.put(dept.getDeptName()+dept.getDeptAffiliation(),dept);
			}
			for (Object readExcel : deptReadExcels) {
				DeptExcelBean deptExcelBean = (DeptExcelBean) readExcel;
				String deptAffiliation = deptExcelBean.getDeptAffiliation();
				//部门名称
				if(deptMap.get(deptExcelBean.getDeptHigherId()+deptExcelBean.getDeptAffiliation())!=null){
					deptExcelBean.setDeptHigherId(deptMap.get(deptExcelBean.getDeptHigherId()+deptExcelBean.getDeptAffiliation()).getId().toString());
				}
				Dept dept = (Dept) ImportExcelUtil.getEntityByExcelBean((DeptExcelBean)readExcel,new Dept());

				//入库
				try {
					Dept deptNew = new Dept();
					BeanUtils.copyProperties(dept, deptNew);
					saveOrUpdate(deptNew);
					deptMap.put(deptNew.getDeptName()+deptAffiliation,deptNew);
					count++;
					int value = count * 100 / size;
					CacheUtil.put(progress, progress, key, value+100);
				} catch (Exception e) {
					log.error(dept + "导入失败");
					CacheUtil.put(progress, progress, key,dept + "导入失败");
					return new AsyncResult<>(new ImportResult(false,dept + "导入失败"));
				}
			}

		} catch (Exception e) {
			log.error(ExceptionUtils.getFullStackTrace(e));
			log.error("解析失败");
			CacheUtil.put(progress, progress, key,"解析失败");
			return new AsyncResult<>(new ImportResult(false,"解析失败"));
		}
		CacheUtil.put(progress, progress, key,"导入成功");
		return new AsyncResult<>(new ImportResult(true,"导入成功"));
	}

	@Override
	public Boolean isHavaChild(long id) {
		Boolean result = false ;
		if (Optional.ofNullable(id).isPresent()) {
			List<Dept> deptList =list(Condition.getQueryWrapper(new Dept()).eq("is_deleted",0).eq("dept_higher_id",id));
			if (deptList.size()>0) {
					result =  true;
			}
		}
		return result;
	}

	@Override
	public IPage<Dept> getAll(IPage<Dept> page, QueryWrapper queryWrapper) {
		return page.setRecords(baseMapper.getAll(page,queryWrapper));
	}

	@Override
	public DeptVO getDetailVO(Object id) {
		return deptWrapper.entityToVO(getById(Long.valueOf(String.valueOf(id))));
	}

	private ImportResult checkData(List<Object> readExcels, List<Dept> depts,String progressId) {
		Map<String,Map<String,String>> mapRef= getRef();
		ImportResult importResult = new ImportResult(true,"");
		List<DeptVO> deptVOList = baseMapper.getAllVO();
		deptVOList.forEach(deptVO ->deptVO.setDeptName(deptVO.getDeptName() + deptVO.getDeptAffiliationName()));
		deptVOList.forEach(deptVO -> deptVO.setDeptCode(deptVO.getDeptCode() + deptVO.getDeptAffiliationName()));
		List<Object> deptListForDup = new ArrayList<>(deptVOList);
		StringBuilder msg = new StringBuilder();
		List<String> codeList = new ArrayList<>();
		List<String> codeForDupList = new ArrayList<>();
		List<String> nameList = new ArrayList<>();
		List<String> nameForDupList = new ArrayList<>();
		for (Object readExcel : readExcels) {
			DeptExcelBean deptExcelBean = (DeptExcelBean) readExcel;
			int index = readExcels.indexOf(readExcel);
			codeList.add(deptExcelBean.getDeptCode());
			nameList.add(deptExcelBean.getDeptName());
			codeForDupList.add(deptExcelBean.getDeptCode() + deptExcelBean.getDeptAffiliation());
			nameForDupList.add(deptExcelBean.getDeptName() + deptExcelBean.getDeptAffiliation());

			//判空
			msg.append(ImportExcelUtil.checkEmpty("2", "部门编码",
				deptExcelBean.getDeptCode(),index).getMsg());
			msg.append(ImportExcelUtil.checkEmpty("2", "部门名称",
				deptExcelBean.getDeptName(),index).getMsg());
			msg.append(ImportExcelUtil.checkEmpty("2", "单位名称",
				deptExcelBean.getDeptAffiliation(),index).getMsg());
			msg.append(ImportExcelUtil.checkEmpty("2", "启用/停用",
				deptExcelBean.getDeptEnableDisable(),index).getMsg());

			//判断类型
			msg.append(ImportExcelUtil.checkType(ImportExcelUtil.TYPE_INTEGER, "2", "启用/停用",
				deptExcelBean.getDeptEnableDisable(), index).getMsg());
			msg.append(ImportExcelUtil.checkType(ImportExcelUtil.TYPE_DATE, "2", "停用时间",
				deptExcelBean.getDeptDeactivationTime(), index).getMsg());
		}

		//判断完类型若有错直接返回
		if (msg.toString().length() > 0) {
			return new ImportResult(false, msg.toString());
		}
		//判重
		//编码是否存在
		msg.append(ImportExcelUtil.isExisted("2","deptCode","部门编码",
			codeForDupList,deptListForDup).getMsg());
		msg.append(ImportExcelUtil.isExisted("2","deptName","部门名称",
			nameForDupList,deptListForDup).getMsg());

		//对象和字典是否存在校验
		List<String> parentNameList = new ArrayList<>();
		int size=readExcels.size();
		int count=0;
		for (Object readExcel : readExcels) {
			DeptExcelBean deptExcelBean = (DeptExcelBean) readExcel;
			int index = readExcels.indexOf(readExcel);
			//对象
			if(mapRef.get("unitRef").containsKey(deptExcelBean.getDeptAffiliation())){
				deptExcelBean.setDeptAffiliation(mapRef.get("unitRef").get(deptExcelBean.getDeptAffiliation()));
				//上级部门
				if (!StringUtil.isEmpty(deptExcelBean.getDeptHigherId())) {
					if (!parentNameList.contains(deptExcelBean.getDeptHigherId()+deptExcelBean.getDeptAffiliation())) {
						if(!mapRef.get("deptRef").containsKey(deptExcelBean.getDeptHigherId()+deptExcelBean.getDeptAffiliation())){
							msg.append("sheet2第"+index+"行(上级部门="+deptExcelBean.getDeptHigherId()+")在所属单位中不存在\n");
						}
					}
				}
				parentNameList.add(deptExcelBean.getDeptName()+deptExcelBean.getDeptAffiliation());
			}else if(!StringUtil.isEmpty(deptExcelBean.getDeptAffiliation())){
				msg.append("sheet2第"+index+"行(单位名称="+deptExcelBean.getDeptAffiliation()+")在系统中不存在\n");
			}

			//是否启用
			if (!StringUtil.isEmpty(deptExcelBean.getDeptEnableDisable())){
				if ("1".equals(deptExcelBean.getDeptEnableDisable()) || "2".equals(deptExcelBean.getDeptEnableDisable())){
					deptExcelBean.setDeptEnableDisable(deptExcelBean.getDeptEnableDisable());
				}else {
					msg.append("sheet2第"+index+"行(启用/停用="+deptExcelBean.getDeptEnableDisable()+")错误\n");
				}
			}

			if (StringUtil.isNotBlank(deptExcelBean.getDeptHead())){
				if (mapRef.get("staffRef").containsKey(deptExcelBean.getDeptHead())) {
					deptExcelBean.setDeptHead(mapRef.get("staffRef").get(deptExcelBean.getDeptHead()));
				}else {
					msg.append("sheet2第"+index+"行(部门负责人="+deptExcelBean.getDeptHigherId()+")在所属单位中不存在\n");
				}
			}
			count++;
			int value = count * 100 / size;
			CacheUtil.put("progress","progress",progressId, value);
		}

		//统一放置报错
		if (msg.toString().length()>0) {
			importResult = new ImportResult(false, msg.toString());
		}
		return importResult;
	}


	public Map<String,Map<String,String>> getRef() {
		Map<String, Map<String, String>> maprefs = new HashMap<>(16);
		Map<String,String> deptRef = new HashMap<>(16);
		for(Dept rank : list()){
			deptRef.put(rank.getDeptName()+rank.getDeptAffiliation(), String.valueOf(rank.getId()));
		}
		maprefs.put("deptRef",deptRef);
		Map<String,String> unitRef = new HashMap<>(16);

		try{
			List<PageData> allOrgs =  orgzationDao.list(null);
			if (CollectionUtil.isNotEmpty(allOrgs)){
				for (PageData pageData : allOrgs){
					unitRef.put((String)pageData.get("name"), (String)pageData.get("id"));
				}

			}
		}catch (Exception e){
			log.error(ExceptionUtils.getFullStackTrace(e));
		}
		maprefs.put("unitRef",unitRef);

		Map<String, String> staffRef = new HashMap<>(16);
		LambdaQueryWrapper<StaffFiles> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(StaffFiles::getCompanyNameId, TokenUtil.getTokenOrgId());
		staffFilesMapper.selectList(queryWrapper).forEach(staffFiles -> {
			staffRef.put(staffFiles.getStaffName(), String.valueOf(staffFiles.getId()));
		});
		maprefs.put("staffRef", staffRef);

		return maprefs ;
	}
	private List<DeptNode> getDeptVOS(long id) {
		List<DeptVO> deptVoList = deptWrapper.entityToVO(baseMapper.selectList(new LambdaQueryWrapper<Dept>().eq(Dept::getDeptHigherId,id)));
		List<DeptNode> list=deptVoList.stream().map(deptVO->new DeptNode(
			deptVO.getId(),null, deptVO.getDeptCode(),deptVO.getDeptName(),deptVO.getDeptAffiliation(),deptVO.getDeptAffiliationName(),null
		)).collect(Collectors.toList());
		list.stream().forEach(d->{
			d.setChildren(getDeptVOS(d.getId()));
		});
		return list;
	}


	/**
	 * 返回当前节点下的所有子节点
	 * @param deptNode deptVO
	 * @param nodes voList
	 */
	private static void getAllChild(DeptNode deptNode, List<DeptNode> nodes) {
		List<DeptNode> list = new ArrayList<>();
		for (DeptNode dept : nodes) {
			if (dept.getParent().equals(deptNode.getId())) {
				getAllChild(dept, nodes);
				list.add(dept);
			}
		}
		deptNode.setChildren(list);
	}
	private List<Object> tree(List<DeptVO> deptVoList){
		List<Object> rootList = new ArrayList<>();
		List<DeptNode> bodyList = new ArrayList<>();
		deptVoList.forEach(deptVO -> {
			if (deptVO.getDeptHigherId() == null) {
				rootList.add(new DeptNode(
					deptVO.getId(),null, deptVO.getDeptCode(),deptVO.getDeptName(),deptVO.getDeptAffiliation(),deptVO.getDeptAffiliationName(),null
				));
			} else {
				bodyList.add(new DeptNode(
					deptVO.getId(),deptVO.getDeptHigherId(), deptVO.getDeptCode(),deptVO.getDeptName(),deptVO.getDeptAffiliation(),deptVO.getDeptAffiliationName(),null
				));
			}
		});
		if (!bodyList.isEmpty()) {
			rootList.forEach(deptNode -> getAllChild((DeptNode) deptNode, bodyList));
		}
		return rootList;
	}

}
